/***********************************************************************
 * Module:  GMainFrame.java
 * Author:  Voja
 * Purpose: Defines the Class GMainFrame
 ***********************************************************************/

package genedit.gui;

import genedit.controller.CommandManager;
import genedit.controller.interfaceActions.GSaveA;
import genedit.controller.interfaceActions.PropertyManager;
import genedit.model.HierarchyElement;
import genedit.model.Package;
import genedit.model.Project;
import genedit.model.Workspace;
import genedit.model.graph.GraphDocument;
import genedit.model.graph.GraphElement;
import genedit.utility.PainterMap;
import genedit.view.graph.GraphDocumentView;
import genedit.view.graph.GraphWorkArea;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.print.PageFormat;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JOptionPane;

import org.xml.sax.InputSource;

import bibliothek.gui.dock.common.CControl;
import bibliothek.gui.dock.common.CGrid;
import bibliothek.gui.dock.common.CWorkingArea;
import bibliothek.gui.dock.common.DefaultMultipleCDockable;
import bibliothek.gui.dock.common.DefaultSingleCDockable;
import bibliothek.gui.dock.common.event.CDockableStateListener;
import bibliothek.gui.dock.common.event.CFocusListener;
import bibliothek.gui.dock.common.event.CVetoClosingEvent;
import bibliothek.gui.dock.common.event.CVetoClosingListener;
import bibliothek.gui.dock.common.intern.CDockable;
import bibliothek.gui.dock.common.mode.ExtendedMode;
import bibliothek.gui.dock.common.theme.ThemeMap;
import bibliothek.util.xml.XElement;
import bibliothek.util.xml.XIO;

//import genedit.utility.PainterMap;

public class GMainFrame extends JFrame {

	/**
	 * @author Voja
	 * 
	 *         GMainFrame - singleton klasa koja upravlja glavnim delom
	 *         korisni�kog interfejsa putem DockingFrames biblioteke. Klasa
	 *         automatski �uva izgled interfejsa
	 */
	private static final long serialVersionUID = -3469327651533302295L;
	protected static GMainFrame mainFrame = null;
	protected CControl controller = null;
	protected CWorkingArea workArea = null;
	protected Set<ManagementElement> documentViewManager = new HashSet<ManagementElement>();

	private ResourceBundle resourceBundle;
	private GToolbar gToolbar;
	private GMainMenu gMainMenu;
	private GMainStatusBar gMainStatusBar;
	private DefaultSingleCDockable dockTree;
	private GProjectTree projectTree;

	protected ManagementElement findManagementElement(DefaultMultipleCDockable dock) {
		ManagementElement result = null;
		for (ManagementElement iter : documentViewManager) {
			if (iter.dock == dock) {
				result = iter;
				break;
			}
		}
		return result;
	}

	protected ManagementElement findManagementElement(GraphDocument document) {
		ManagementElement result = null;
		for (ManagementElement iter : documentViewManager) {
			if (iter.graphDocumentView.getModel() == document) {
				result = iter;
				break;
			}
		}
		return result;
	}

	public GraphDocumentView getGraphDocumentView(GraphDocument document){
		ManagementElement element=findManagementElement(document);
		if(element==null)
			return null;
		return element.graphDocumentView;
	}
	
	public boolean contains(GraphDocument document) {
		if (findManagementElement(document) != null)
			return true;
		return false;
	}

	public boolean setFocusedDocument(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return false;
		element.dock.toFront();
		return true;
	}

	public void refreshAllViews() {
		for (ManagementElement el : documentViewManager) {
			el.graphDocumentView.getWorkArea().repaint();
		}
	}

	public Dimension getGraphDocumentViewDimension(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return null;
		if (!element.dock.isVisible())
			return new Dimension(0, 0);
		return new Dimension(element.graphDocumentView.getWorkArea().getWidth(),
				element.graphDocumentView.getWorkArea().getHeight());
	}

	// pokupi naslov dokumenta
	public void changeTitle(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		element.dock.setTitleText(document.getName());
	}

	public void documentChanged(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return;
		String title = element.dock.getTitleText();
		if (!title.startsWith("*"))
			title = "*" + title;
		element.dock.setTitleText(title);
	}

	public void documentSaved(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return;
		String title = element.dock.getTitleText();
		if (title.startsWith("*"))
			title = title.substring(1);
		element.dock.setTitleText(title);
	}

	public boolean isDocumentChanged(GraphDocument document) {
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return false;

		String title = element.dock.getTitleText();
		return title.startsWith("*");
	}

	public void addGraphDocumentView(GraphDocument document) {
		if (document == null)
			return;
		if (contains(document))
			return;

		GraphDocumentView newGraphDocumentView = new GraphDocumentView(document);

		ManagementElement managementEl = new ManagementElement();
		managementEl.graphDocumentView = newGraphDocumentView;
		managementEl.dock = new DefaultMultipleCDockable(null, newGraphDocumentView.getWorkArea());
		managementEl.dockId = getFreeNextKey();
		managementEl.dock.setTitleText(newGraphDocumentView.getModel().getName());
		managementEl.dock.setCloseable(true);
		managementEl.dock.setExternalizable(false);
		managementEl.dock.setMinimizable(false);
		managementEl.dock.setMaximizable(true);
		// managementEl.dock.setStackable(false);
		// managementEl.dock.setSticky(false);
		managementEl.dock.setStickySwitchable(false);

		managementEl.dock.addFocusListener(new CFocusListener() {
			@Override
			public void focusLost(CDockable dock) {

			}

			// ako je dobio fokus da postane Aktivni dokument
			@Override
			public void focusGained(CDockable dock) {
				if (dock instanceof DefaultMultipleCDockable) {
					ManagementElement managementEl = findManagementElement((DefaultMultipleCDockable) dock);
					if (managementEl != null) {
						Workspace.getInstance().setActiveDocument(managementEl.graphDocumentView);
					}
				}
			}
		});
		managementEl.dock.addVetoClosingListener(new CVetoClosingListener() {
			@Override
			public void closing(CVetoClosingEvent ev) {
				for (int i = 0; i < ev.getDockableCount(); i++) {
					if (ev.getDockable(i) instanceof DefaultMultipleCDockable) {
						ManagementElement element = findManagementElement((DefaultMultipleCDockable) ev
								.getDockable(i));
						if (element != null) {
							if (saveDocumentCheck(element)) {
								documentViewManager.remove(element);
							} else {
								ev.cancel();
							}
						}
					}
				}
			}

			@Override
			public void closed(CVetoClosingEvent arg0) {
				// TODO Auto-generated method stub

			}
		});

		// loadElements
		for (GraphElement element : document.getElements()) {
			newGraphDocumentView.addElementPainters(PainterMap.getInstance().getPainter(element));
		}

		documentViewManager.add(managementEl);

		workArea.show(managementEl.dock);
		managementEl.dock.toFront();

	}

	public void removeGraphDocumentView(GraphDocument document) {
		if (document == null)
			return;
		ManagementElement element = findManagementElement(document);
		if (element == null)
			return;
		element.dock.setVisible(false);
		controller.removeDockable(element.dock);
		documentViewManager.remove(element);
	}

	public void closeViewsOfClosedDocuments(HierarchyElement hierarchyElement) {
		if (hierarchyElement instanceof GraphDocument) {
			removeGraphDocumentView((GraphDocument) hierarchyElement);
			return;
		}
		if (hierarchyElement instanceof Package) {
			Package pack = (Package) hierarchyElement;
			Iterator<HierarchyElement> it = pack.getIteratorDocuments();
			while (it.hasNext())
				closeViewsOfClosedDocuments(it.next());
		}
		if (hierarchyElement instanceof Project) {
			Project project = (Project) hierarchyElement;
			Iterator<Package> it = project.getIteratorPackages();
			while (it.hasNext())
				closeViewsOfClosedDocuments(it.next());
		}
	}

	public void removeAllGraphDocumentView() {
		for (Iterator<ManagementElement> iter = documentViewManager.iterator(); iter.hasNext();)
			removeGraphDocumentView(((ManagementElement) iter.next()).graphDocumentView.getModel());
	}

	protected GMainFrame() {
		resourceBundle = ResourceBundle.getBundle("genedit.gui.MessageResources.MessageResources",
				new Locale("en", "US"));
		// inicijalizacija kontrolera
		controller = new CControl();
		controller.setTheme(ThemeMap.KEY_ECLIPSE_THEME);

		this.add(controller.getContentArea());
		boolean hasSavedSettings = false;
		// TODO loadSettings

		if (!PropertyManager.getInstance().propertyFileExists()) {
			PropertyManager.getInstance().initManager();
		} else {
			try {
				PropertyManager.getInstance().load();
			} catch (Exception e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
		}
		initialization();

		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				super.windowClosing(e);
				GMainFrame.getInstance().saveSettings();
			}
		});
	}

	public void changeLanguage() {

		resourceBundle = ResourceBundle.getBundle("genedit.gui.MessageResources.MessageResources",
				Locale.getDefault());
		gMainMenu.initComponents();
		// gMainStatusBar.initComponents();
		gToolbar.initComponents();
		dockTree.setTitleText(resourceBundle.getString("ProjectExplorer"));
		// kada se odabere zeljeni jezik hajduje submenu
		JMenu m = (JMenu) gMainMenu.findItemByName("subMenuLanguage");
		m.getPopupMenu().setVisible(false);

	}

	// save settings (on exit)
	protected void saveSettings() {
		PropertyManager.getInstance().setProperty(PropertyManager.MainFrameDimension,
				new Dimension(GMainFrame.getInstance().getSize()));
		PropertyManager.getInstance().setProperty(PropertyManager.MainFrameLocation,
				new Point(GMainFrame.getInstance().getLocation()));
		PropertyManager.getInstance().setProperty(PropertyManager.MainFrameExtendedState,
				new Integer(GMainFrame.getInstance().getExtendedState()));

		// nema potrebe za cuvanjem ProjectTree property-jam, on se automatski
		// cuva

		// cuvanje property-a u fajl
		try {
			PropertyManager.getInstance().save();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// sacuvaj layout
		String layoutPath = (String) PropertyManager.getInstance().getProperty(
				PropertyManager.LayoutPath);
		XElement xml = new XElement("layout");
		controller.writeXML(xml);
		try {
			PrintWriter stream = new PrintWriter(new BufferedOutputStream(new FileOutputStream(
					new File(layoutPath))));
			stream.write(xml.toString());
			stream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void initialization() {
		gToolbar = GToolbar.getInstance();
		gMainMenu = GMainMenu.getInstance();
		gMainStatusBar = GMainStatusBar.getInstance();
		projectTree = GProjectTree.getInstance();
		if (Locale.getDefault().equals(new Locale("en", "US")))
			GMainMenu.getInstance().getRadioItemEnglish().setSelected(true);
		else
			GMainMenu.getInstance().getRadioItemSerbian().setSelected(true);

		// inicijalna podesavanja glavne forme
		setTitle("GenEdit 1.0");
		setSize((Dimension) PropertyManager.getInstance().getProperty(
				PropertyManager.MainFrameDimension));
		setLocation((Point) PropertyManager.getInstance().getProperty(
				PropertyManager.MainFrameLocation));
		setExtendedState(getExtendedState()
				| (Integer) PropertyManager.getInstance().getProperty(
						PropertyManager.MainFrameExtendedState));
		setDefaultCloseOperation(EXIT_ON_CLOSE);

		// dodavanje toolbar-a i statusbar-a
		gMainStatusBar.setPreferredSize(new Dimension(getWidth(), 16));
		add(gMainStatusBar, BorderLayout.SOUTH);
		add(gToolbar, BorderLayout.NORTH);

		// dodavanje menija
		setJMenuBar(gMainMenu);

		// kreiranje radne povr�ine
		workArea = controller.createWorkingArea("workArea");
		CGrid grid = new CGrid(controller);

		dockTree = new DefaultSingleCDockable("projectTree", projectTree.getScrollPane());
		dockTree.setTitleText(/* "Project explorer" */resourceBundle.getString("ProjectExplorer"));
		dockTree.setCloseable(true);
		dockTree.setExternalizable(false);
		dockTree.setMinimizable(false);
		dockTree.setMaximizable(true);
		// dockTree.setStackable(false);
		// dockTree.setSticky(false);
		dockTree.setStickySwitchable(false);

		grid.add(0, 0, 1, 4, dockTree);
		grid.add(1, 1, 3, 3, workArea);

		controller.getContentArea().deploy(grid);
	}

	// dodatna metoda za inicijalizaciju glavnog interfejsa
	// uvdedena radi spre�avanja medjurekurzivnih poziva getInstance() metode
	// ovde je deo koda koji sluzi inicijalizaciji a moze da prouzrokuje
	// pozivanje raznih listenera
	protected void additionalInitialization() {
		// load saved layout (if exists)
		File f = new File((String) PropertyManager.getInstance().getProperty(
				PropertyManager.LayoutPath));
		if (f.exists()) {
			String layoutPath = (String) PropertyManager.getInstance().getProperty(
					PropertyManager.LayoutPath);
			try {
				String[] layouts = controller.layouts();
				for (String s : layouts) {
					controller.delete(s);
				}
				XElement xml = XIO.read(new InputSource(new FileInputStream(new File(layoutPath))));
				controller.readXML(xml);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		JCheckBoxMenuItem projectTreeVisible = (JCheckBoxMenuItem) gMainMenu
				.findItemByName("checkItemProjectTree");
		boolean isSelected = (Boolean) PropertyManager.getInstance().getProperty(
				PropertyManager.ProjectTreeVisible);
		projectTreeVisible.setSelected(isSelected);
		if (!isSelected)
			dockTree.setVisible(false);
		// hide listener
		// dodaje se tek posto se utvrdi da li ce projectTree biti vidljiv
		// izbegavaju se rekurzivni pozivi
		dockTree.addCDockableStateListener(new CDockableStateListener() {
			@Override
			public void visibilityChanged(CDockable dock) {
				JCheckBoxMenuItem cbmi = (JCheckBoxMenuItem) GMainMenu.getInstance()
						.findItemByName("checkItemProjectTree");
				cbmi.setSelected(dock.isVisible());
			}

			@Override
			public void extendedModeChanged(CDockable arg0, ExtendedMode arg1) {

			}
		});
	
		loadWorkspace();
	}
	
	public void loadWorkspace(){
		File workspace=Workspace.getInstance().getPath();
		for(File projectFile:workspace.listFiles()){
			String extension = "";

			int index = projectFile.getName().lastIndexOf('.');
			if (index > 0) {
			    extension = projectFile.getName().substring(index+1);
			    if(extension.equals("proj")||extension.equals("xml")){
					Project project = null;
					try {
						if (projectFile.getCanonicalPath().contains(".xml")) {
							BufferedReader in = new BufferedReader(new InputStreamReader(
									new FileInputStream(projectFile)));
							project = (Project) Workspace.getInstance().getXstream().fromXML(in);
						} else {
							ObjectInputStream in = null;
							in = new ObjectInputStream(new FileInputStream(
									projectFile.getCanonicalPath()));
							project = (Project) in.readObject();
							in.close();
						}
					} catch (Exception e) {
						//fajl nije odgovarajuceg tipa
					}
					if(project!=null){
						try {
							project.setPath(projectFile.getCanonicalPath());
						} catch (IOException e1) {
							e1.printStackTrace();
						}
		
						Workspace.getInstance().addProjects(project);
						project.setParent(Workspace.getInstance());
					}
			    }
			}
		}
	}
	
	public static String getFreeNextKey() {
		int key = 0;
		while (mainFrame.controller.getMultipleDockable("document_" + key++) != null)
			;

		return "document_" + (key - 1);
	}

	protected boolean saveDocumentCheck(ManagementElement element) {
		if(element.graphDocumentView.getModel().getInvoker().getCommands().size()==0){
			return true;			
		}
		//Custom button text
		Object[] options = {resourceBundle.getString("AnswerYes"),
				resourceBundle.getString("AnswerNo"),
				resourceBundle.getString("AsnwerCancel")};
		int answer = JOptionPane.showOptionDialog(this,
			element.graphDocumentView.getModel()+" "+resourceBundle.getString("QuestionSaveDocument"),
			resourceBundle.getString("QuestionSaveDocumentName"),
		    JOptionPane.YES_NO_CANCEL_OPTION,
		    JOptionPane.QUESTION_MESSAGE,
		    null,
		    options,
		    options[2]);
		if(answer==0){
			GSaveA.getInstance().actionPerformed(null);
			return true;
		}
		if(answer==1){
			CommandManager invoker=element.graphDocumentView.getModel().getInvoker();
			while(invoker.getCurrentCommandIndex()!=-1){
				invoker.undo();
			}
			return true;
		}
		//na cancel odustani od zatvaranja
		return false;
	}

	public static GMainFrame getInstance() {
		if (mainFrame == null) {
			mainFrame = new GMainFrame();
		}
		return mainFrame;
	}

	public CControl getController() {
		return controller;
	}

	public static void main(String[] args) {
		GMainFrame mf = GMainFrame.getInstance();
		mf.additionalInitialization();
		mf.setVisible(true);
	}

	protected static class ManagementElement {
		public GraphDocumentView graphDocumentView;
		public String dockId;
		public DefaultMultipleCDockable dock;
	}

	public ResourceBundle getResourceBundle() {
		return resourceBundle;
	}

	public void setResourceBundle(ResourceBundle resourceBundle) {
		this.resourceBundle = resourceBundle;
	}

	public DefaultSingleCDockable getDockTree() {
		return dockTree;
	}

	public void setDockTree(DefaultSingleCDockable dockTree) {
		this.dockTree = dockTree;
	}

	public GProjectTree getProjectTree() {
		return projectTree;
	}

	public void setProjectTree(GProjectTree projectTree) {
		this.projectTree = projectTree;
	}

	public GToolbar getgToolbar() {
		return gToolbar;
	}

	public void setgToolbar(GToolbar gToolbar) {
		this.gToolbar = gToolbar;
	}

	public GMainMenu getgMainMenu() {
		return gMainMenu;
	}

	public void setgMainMenu(GMainMenu gMainMenu) {
		this.gMainMenu = gMainMenu;
	}

	public GMainStatusBar getgMainStatusBar() {
		return gMainStatusBar;
	}

	public void setgMainStatusBar(GMainStatusBar gMainStatusBar) {
		this.gMainStatusBar = gMainStatusBar;
	}
	
	/**
	 * Metoda za stampanje aktivnog dokumenta
	 * 
	 */
	public void print(){
		  PrinterJob printJob = PrinterJob.getPrinterJob();
		  PageFormat pageFormat = printJob.pageDialog(printJob.defaultPage());
		  printJob.setPrintable((GraphWorkArea)Workspace.getInstance().getActiveDocument().getWorkArea(), pageFormat);	
		  if (printJob.printDialog())
			  try {
				  printJob.print();
			  }
	      	  catch (PrinterException pe) {
	      		  System.out.println("Error printing: " + pe);
	      	  }
	  }

}